The universal quandary bedeviling every language designer is how to provide support for some given functionality within the language. Two obvious strategies present themselves. Implement the feature as a language primitive or ask the language user to construct the function from other language primitives when it is needed.
For example, suppose we are designing a language and decide we need the XOR functionality. Of course, we can create a language primitive called XOR. But we can also decide that since we have
already provided functionality primitives AND, OR and NOT there is no need to go further. We simply tell the language user to build the XOR functionality from the other available primitives each time it is needed. The trade-off is obvious. The language, and the tools that support the language, can be more complex and harder to develop or, the language user can expend more time and effort constructing models because they have to build complex functionality out of simpler primitives.
Although the solution to this example may be obvious, such isn't always the case. Obviously, language users would prefer a language be packed with primitives representing all their favorite functionality. Language designers and tool implementers on the other hand obviously desire that a language contain only the minimum necessary set of primitives. Where is the optimum solution?
One highly desirable feature of a system design language is the ability to dynamically establish bi-directional connectivity between various system functions. This allows the specification of time varying logical connectivity between the various operations contained within a system level model without concern about how the physical implementation might be accomplished. This capability is also useful at lower levels of design abstraction. For instance, it is often necessary to model pass gate behavior at the switch level. Recently we wanted to model just such behavior using VHDL and Verilog HDL. It turns out to be a difficult model to describe correctly and a very resource intensive model to simulate. For the coding details see Appendix A, B, and C (Please find Appendix B and C online at www.isdmag.com/editorial/2001/systemdesign0106a.html).
Anyone who has tried to model pass gates in VHDL and Verilog HDL knows that these languages don't provide good descriptive mechanisms for describing bi-directional connectivity. A bi-directional pass gate is nothing more than a wire between two electrical nodes. If a control signal is active, the wire connects the two nodes. If disabled, the wire is removed. In VHDL this is the equivalent of trying to assign two inout ports to each other and anyone who has tried it knows that it is impossible. In creating the pass gate model we had significant problems, both in VHDL and Verilog HDL, using existing language constructs to describe (write, read, debug) the behavior we wanted. We were also very disappointed in the simulation speed of the model we finally created.
Verilog HDL provides two mechanisms to model bi-directional connectivity. One is a set of language primitives named tran, tranif1, rtran, and so forth. The other mechanism is to describe the bi-directional connectivity with other language primitives.
Most likely, this description would be encapsulated within a component to facilitate design reuse. VHDL contains nothing like the tran primitive. However, it is possible to describe the bi-directional connectivity with other language primitives and use the same component mechanism that Verilog HDL has.
Example of the Verilog HDL tranif1
tranif1 (node_a, node_b, control_x)
tranif1 (node_a, node_c, control_y)
Examples of VHDL and Verilog components
Architecture top
...
u1: pass_tran port map (node_a, node_b,
control_x);
u2: pass_tran port map (node_a, node_c,
control_y);
Module top
...
u1 pass_tran (node_a, node_b, control_x);
u2 pass_tran (node_a, node_c, control_y);
It goes without saying that in the case of the component instantiations, the devil is in the details. The hard task of constructing the bi-directional connectivity functionality still remains to be done.
Unfortunately, just the code written so far has already obscured the basic nature of the bi-directional connectivity intended in our behavioral model. This is because both of the mechanisms used above are
structural in nature and there is no way to embed this functionality within the flow of some behavioral code.
If, in some behavioral model, the intent is to first establish connectivity between node_a and node_b and then later between node_a and node_c, we must say:
control_x = 1
control_y = 0
...
control_x = 0
control_y = 1
and then we must stop and refer to the structural code above that will exist elsewhere else in the model. Only then can we finally understand the intent of these obscure control signal assignments.
Alternatively, we could embed the entire behavioral description of the bi-directional connectivity that is
encapsulated in the component instantiation at the point in the behavioral model where we enabled the control line. That, however, is even more confusing because of the complexity of the code inside the
component. It would be much nicer if we could say:
connect (node_a, node_b)
...
disconnect (node_a, node_b)
connect (node_a, node_c)
as part of the thread of execution of the behavioral model where the connect and disconnect statements dynamically create or remove communication paths without further effort on the part of the modeler.
The issues surrounding bi-directional connectivity functionality very clearly illuminate the problems facing the designers of VHDL and Verilog HDL. VHDL designers took the path of asking language users to implement the bi-directional connectivity functionality with other VHDL language constructs.
The Verilog HDL designers took the middle ground, providing a limited use primitive that is useful at a low level of modeling abstraction but which isn't very useful at higher levels of abstraction.
In hindsight, we can perhaps assume that the VHDL designers realized that implementing this primitive in a VHDL simulator was difficult and that the functionality wouldn't be widely used. They argue that there isn't enough utility to justify its inclusion. The Verilog HDL designers saw a specific need and addressed it but didn't see a need to provide a more general purpose construct that would have required more work to properly implement.
On the other hand, as can be seen from the models we created, there is a significant coding and runtime penalty involved in using other language primitives to implement the bi-directional connectivity functionality. We argue that there is enough utility to justify its inclusion -- that this function is so useful that it deserves to be a simulator primitive and it shouldn't be necessary for coders to construct it using other language primitives.
Certainly, having language primitives to represent the bi-directional connectivity functionality would radically simplify the pass gate description, improve simulator performance, and increase designer productivity.
So who's correct? For this functionality? For any other functionality that might be proposed?
Appendix A: Modeling details of the pass gate implementation in VHDL and Verilog HDL
After several attempts to create a model for the pass gate, we finally found one that worked -- but they're not elegant. In fact, they're downright ugly. These models have two significant problems. The first problem is the necessary existence of an artificial clock signal, running always when X value appears on the port (pots). The clock is used to sample both external inputs to the pass gate and determine the next state of the device. Unfortunately, that means that this model must execute every simulation time unit even if the inputs don't change. That's a lot of overhead. The other problem is the necessary addition of several artificial signals into the model to keep track of what's going on. That's more overhead.
These are VERY BAD modeling decisions but... we don't have any other choices for trying to create a model using VHDL or Verilog HDL! Within the restrictions of the current version of both languages, there is no other way.
A better solution to this problem would be to add additional constructs to the languages to allow two
signals to be temporarily connected or disconnected within the model. For instance, if we had statements like "connect (A,B)" or "disconnect (A,B)" in VHDL and Verilog HDL then all this code becomes unnecessary.
Resolution function
The key to the model isn't the bi-directional data transfer -- the inout ports take care of that -- it is the process of determining and feeding the correct data to the inout ports that is hard (neutralization of port stuck in X(undefined) state).
This process consists from two iterations:
1) Z value "injection" on both ports to determine the values driving the two external ports and ...
2) resolution of the external values and assignment of the resolved value back to the ports.
As you can see, sensitivity list of RP has not only those ports, but also some artificial signal NEW_TIME.
This signal has event (transition 0 -> 1) when we have first simulation cycle for each simulation macro time, defined by our internal clock "clk."
So, event on this signal makes RP to do iteration (1). This is key point and it is ...
To explain why we are so happy having this very bad implementation, we need to show what we wanted to have. Ideal is the situation in which switch knows exactly when it needs to start RP. Usually it is time, when ports have events. But if ports have X value, this value doesn't allow to any values from external world to reach switch (to create any events on ports -- switch becomes deaf)! That is why we want to use any other possibilities, maybe not direct, which will allow us to know that time or, at least not to miss really necessary time point.
In event driven simulation, simulation time points represent time, when any signal changes happen.
So, if we have some variable (signal), which has event on every time, when we have first simulation cycle for new macro time, we at least sure that we don't miss real time of nets (connected with switch ports) changing values.
That is why we have signal NEW_TIME. This signal must have value equal 1 during first simulation cycle of new macro time, and 0 -- all others. So, we have to make switch to be simulated all the time points -- it is already bad decision!
If $time system task for Verilog (variable NOW for VHDL) created events when they changed, then it would be enough to have
assign NEW_TIME = $time != LAST; // Verilog
NEW_TIME <= NOW /= LAST; -- VHDL
but they don't create, so we MUST create internal clock, which will make to work assignment statement for NEW_TIME. Clock period has to be small enough not to miss any events!
Yuri Tatarnikov is a Technical Manager of Intrinsix Corp. Yuri previously worked as the head of CAD departments in various EDA enterprises in Russia and was the founder and director of the VHDL Users Group in Russia. He is an author of Hardware description language MODIS-V78 (Russian State Standard) and the MODIS-VES simulator. He has published over 70 technical papers on electronic design methods and simulation.
Larry Saunders is a recognized VHDL applications
expert with 23 years experience in computer design and EDA tools development and usage. He spent 14 years with IBM and then 6 years training and consulting with various EDA vendors and hardware design companies. After 14 years with IBM, he has spent the last six years He served as chair of the IEEE 1076-1987 VHDL Standardization effort, was founding member and chair of the VHDL Users group (now VHDL International User's Forum -VIUF ), and chair of the Design Automation Standards Committee of the IEEE.
Please note: The bi-directional pass gate VHDL and Verlog models are given in Appendix B and C, which are omitted here, but can be found in the full version at: www.isdmag.com/editorial/2001/systemdesign0106a.html